#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define TAM 500

//struct usada para os parametros de cada thread
typedef struct th{
    int sockfd;
    int newsockfd;
    int portno;
    int *v;
    int n;
}threadptr;

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

void *func(void*t)
{
	struct sockaddr_in serv_addr, cli_addr;
	socklen_t clilen;
	threadptr *thread = (threadptr*) t;
	int n, i;
	
	thread->sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (thread->sockfd < 0)
		error("ERRO ao abrir socket");
		
	bzero((char*)&serv_addr,sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(thread->portno);
    
    if (bind(thread->sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0)
        error("ERROR on binding");
    listen(thread->sockfd,5);
    clilen = sizeof(cli_addr);
    thread->newsockfd = accept(thread->sockfd,
    				(struct sockaddr*) &cli_addr,
    				&clilen);
    if(thread->newsockfd <0)
    	error("ERRO ao aceitar");
    
    thread->v = malloc(sizeof(int)*(TAM/2));
    
    n = recv(thread->newsockfd,thread->v,sizeof(int)*(TAM/2),MSG_WAITALL);
    if(n<0)
    	error("ERROR ao escrever no socket");
    
    n = write(thread->newsockfd,"OK", 3);
    
}

//funcao usada para o qsort
int compare(const void *a, const void *b)
{
	return (*(int*)a - *(int*)b);
}

int main (int argc, char *argv[])
{
	struct timeval tmr0, tmr1;

	if(argc < 5)
	{
		fprintf(stderr,"numero invalido de argumentos");
    	exit(0);
	}
	//int sockfd, newsockfd, portno;
	//int sockfd1, newsockfd1;
	int *vet1,*vet2;
	int res1, res2, i;
	double tempo;
	int vetor[TAM];
	pthread_t thread1, thread2;
	threadptr t1, t2;
	
	//argvs:
	//1 = nome da porta de cima
	//2 = porta de baixo nro1
	//3 = porta de baixo nro2
	//4 = porta de cima
	t1.portno = atoi(argv[2]);
	t2.portno = atoi(argv[3]);
	
	res1 = pthread_create(&thread1, NULL, func, (void*)&t1);
	res2 = pthread_create(&thread2, NULL, func, (void*)&t2);
	
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	
	vet1 = malloc(sizeof(int)*(TAM/2));
	vet2 = malloc(sizeof(int)*(TAM/2));
	
	vet1 = t1.v;
	vet2 = t2.v;
	
	for(i=0;i<(TAM/2);i++)
	{
		vetor[i] = vet1[i];
		vetor[i+(TAM/2)] = vet2[i];
	}
	
	gettimeofday(&tmr0, 0);
	qsort(vetor,TAM,sizeof(int),compare);
	gettimeofday(&tmr1, 0);
	tempo = 1000000*(tmr1.tv_sec - tmr0.tv_sec) + (tmr1.tv_usec - tmr0.tv_usec);
	printf("\nTempo_ord_nivel3: %.2f us\n", tempo);
	
	//iniciando conexao com o nivel superior
	int final[1000];
	char buffer[256];
	int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
	
	portno = atoi(argv[4]);
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
		error("ERROR abrindo socket");
	server = gethostbyname(argv[1]);
	if(server == NULL)
	{
		fprintf(stderr,"ERROR nao ha host");
		exit(0);	
	}
	bzero((char *) &serv_addr, sizeof(serv_addr));
     serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
          (char *)&serv_addr.sin_addr.s_addr,
          server->h_length);
    serv_addr.sin_port = htons(portno);
    
	gettimeofday(&tmr0, 0);
	n = connect(sockfd,(struct sockaddr*) &serv_addr, sizeof(serv_addr));
    if(n<0)
    	error("ERROR ao conectar");
    n=write(sockfd,vetor,sizeof(int)*TAM); //envia vetor pra cima
    if(n<0)
    	error("ERROR ao escrever");
    n=recv(sockfd,buffer,3,MSG_WAITALL); //recebe msg de confirmacao
    if(n<0)
    	error("ERROR ao receber");
	gettimeofday(&tmr1, 0);
	tempo = 1000000*(tmr1.tv_sec - tmr0.tv_sec) + (tmr1.tv_usec - tmr0.tv_usec);
	printf("\nTempo_conexao_34: %.2f us\n", tempo);

    printf("%s\n",buffer);
	
	gettimeofday(&tmr0, 0);	
	n=recv(sockfd,final,sizeof(int)*1000,MSG_WAITALL); //recebe vetor final
	if(n<0)
    	error("ERROR ao receber");
	gettimeofday(&tmr1, 0);
	tempo = 1000000*(tmr1.tv_sec - tmr0.tv_sec) + (tmr1.tv_usec - tmr0.tv_usec);
	printf("\nTempo_conexao_34: %.2f us\n", tempo);
    
    //enviando aos niveis inferiores
	gettimeofday(&tmr0, 0);	
    n = write(t1.newsockfd,final,sizeof(int)*1000);
    if(n<0)
    	error("ERROR ao escrever");
    n = write(t2.newsockfd,final,sizeof(int)*1000);
    if(n<0)
    	error("ERROR ao escrever");
	gettimeofday(&tmr1, 0);
	tempo = 1000000*(tmr1.tv_sec - tmr0.tv_sec) + (tmr1.tv_usec - tmr0.tv_usec);
	printf("\nTempo_conexao_32: %.2f us\n", tempo);
    
    close(sockfd);
    close(t1.sockfd);
    close(t2.sockfd);
    close(t1.newsockfd);
    close(t2.newsockfd);
	pthread_exit(NULL);
	return 0;
}